const TArray = {

  "NumArray": class NumArray extends Array{
    // 参数：
    // arr   : 数组
    // shape : 形状
    constructor(arr, shape){
      super(0);

      this.shape = [];

      if(arr !== undefined){
        this.copyFromArray(arr);
      } 
      
      if(shape instanceof Array){
        this.reshape(shape);
      }
    }

    // 从列表复制数据
    copyFromArray(arr){
      if(arr instanceof Array){
        this.length = 0;
        this.shape  = [];
        this._pushData(arr, 0);
      }
      else{
        throw new Error('参数不是一个列表');
      }
    }

    // 从2维数组中复制数据
    // 仅复制数据
    // 不修改形状
    _copyFromMat(mat){
      this.length = 0;
      for(var i = 0, il=mat.length; i < il; i++){
        for(var j = 0, jl = mat[i].length; j < jl; j++){
          this.push(mat[i][j]);
        }
      }
    }
    
    // 添加数据
    _pushData(dat, dim){
      for(let i = 0, il = dat.length; i < il; i++){
        this.shape[dim] = dat.length;
        if(dat[i] instanceof Array){
          this._pushData(dat[i], dim + 1);
        }
        else{
          this.push(parseInt(dat[i]));
        }
      }
    }
    // 改变形状
    // 参数：
    // shape: 形状参数
    reshape(shape){
      var tl = TArray.mulAll(shape);
      // 数据为空时
      // 直接修改形状
      if(this.length === 0){
        this.shape  = [...shape];
        this.length = tl;
      }
      else{
        if(tl <= this.length){
          this.shape = [...shape];
        }
        else{
          // console.error(`数据长度${this.length},形状${shape}`);
          throw new Error(`
          无效的形状类型:${shape}
          没有足够的数据, 数据长度为${this.length}`);
        }
      }
    }

    // 分割数据并返回分割后的数组
    // 参数：
    // n : 要将数据分割为多少块
    _splitData(dat, n){

      let blockSize = Math.floor(dat.length / n);
      
      let subArr = [];
      for(let i = 0; i < n; i++){
        let offset = i * blockSize;
        subArr[i] = dat.slice(offset, offset + blockSize);
      }

      return subArr;
    }

    // 按照形状分割数据
    _splitToArray(dat, shape){
      dat = this._splitData(dat, shape[0]);
      for(let i = 0; i < shape[0]; i++){
        if(shape.length > 2){
          dat[i] = this._splitToArray(dat[i], shape.slice(1));
        }
      }

      return dat;
    }

    // 转换为数组
    toArray(){
      let arr = this.cloneData();
      // 特别处理向量
      if(this.dim() === 2 
      && (this.shape[0] === 1 || this.shape[1] === 1)){
        return arr;
      }
      arr = this._splitToArray(arr, this.shape);
      return arr;
    }

    // 数据克隆
    cloneData(){
      let arr = [...this];
      return arr;
    }

    // 克隆数组
    clone(){
      return new NumArray(this, this.shape);
    }

    // 返回维度值
    dim(){
      return this.shape.length;
    }

    // 获取对应维度数据长度
    _axisL(axis){
      var bs = 1;
      if(axis === 0){
        return 1;
      }

      for(let i = 0; i < axis && i < this.shape.length; i++){
        bs *= this.shape[this.shape.length - 1 - i];
      }

      return bs;
    }

    // 维度对应的数据块计数
    _axisN(axis){
      var i = this.shape.length - 1 - axis;
      if( axis > this.shape.length - 1 ){
        return 1;
      }
      return this.shape[i];
    }

    // 维度对应的数据列表
    _axisD(axis){
      if(axis >= this.dim()){
        throw new Error(`维度超过最大值${this.dim() - 1}`);
      }

      var blockSize = this._axisL(axis);
      var tmp = this.cloneData();
      let arr = [];
      for(let i = 0; i < (this.length / this._axisL(axis)); i++){
        arr[i] = tmp.slice(i*blockSize, (i+1)*blockSize);
      }

      return arr;

    }

    // 返回数据索引值
    _index(idxs){
      var idx = 0;
      for(let i = 0, il = idxs.length; i < il; i++){
        idx += idxs[i] * this._axisL(this.dim() - 2 - i);
      }
      return idx;
    }

    // 获取指定位置的数据
    value(...args){
      if(this.dim() === 1){
        return this[args[0]];
      }

      if(args.length !== this.dim()){
        throw new Error(`请求数据维度不匹配:
          请求维度${args.length}, 
          数据维度${this.dim()}`);
      }

      return this[this._index(args)];
    }

    // 修改指定位置的数据值
    setValue(val, ...args){
      if(this.dim() === 1){
        this[args[0]] = val;
        return this[args[0]];
      }

      if(args.length !== this.dim()){
        throw new Error(`请求数据维度不匹配:
          请求维度${args.length}, 
          数据维度${this.dim()}`);
      }

      const idx = this._index(args);
      this[idx] = val;
      return this[idx];
    }

    // 重复某个维度的数据
    _tileAxis(axis, reps){
      const dats = this._axisD(axis);
      var mat=[];
      for(var i = 0, il = dats.length; i < il; i++){
        mat[i] = [];
        for(var n = 0; n < reps; n++){
          mat[i].push(...dats[i])
        }
      }
      
      this._copyFromMat(mat);

      this.shape[this.dim() - 1 - axis] *= reps;

      return mat;
    }

    // 平铺数据
    _tile(reps){
      if(!(reps instanceof Array)){
        reps = [reps];
      }

      if(this.dim() < reps.length){
        // 增加数据维度
        while(this.dim() !== reps.length){
          this.shape.splice(0, 0, 1);
        }
      }

      for(var i = 0, il = reps.length; i < il; i++){
        this._tileAxis(il - 1 - i, reps[i])
      }
    }

    // 行数据
    _rows(){
      if(this.dim() === 1){
        return this.cloneData();
      }

      return this._axisD(this.dim() - 1);
    }

    //
    _doSlice(arr, shape, slices, newShape, d){
      if(slices.length <= 0){
        return arr;
      }

      var tn = new NumArray(arr, shape);
      var dats = tn._rows();
      let ret = [];

      var range = slices[0].split(':');
      var begin = parseInt(range[0]);
      var end = range[1];

      if(end === undefined){
        end = begin + 1;
      }

      if(end <= 1 || isNaN(end)){
        end = dats.length;
      }

      if(slices.length > 1){
        for(let i = begin, idx = 0; i < end; i++, idx++){
          ret[idx] = this._doSlice(
            dats[i], 
            shape.slice(1), 
            slices.slice(1),
            newShape,
            d+1
            );
        }
      }
      else{
        ret = dats.slice(begin, end);
      }
   
      newShape[d] = end - begin;

      return ret;
    }

    // 数据分割
    slice(...args){
      var newShape = [];
      // 分割数据
      var dats = this._doSlice(
        this.cloneData(), 
        this.shape, 
        args,
        newShape,
        0
      );
      // 计算形状
      while(newShape.length < this.shape.length){
        newShape.push(this.shape[newShape.length]);
      }
      var ret = new NumArray(dats, newShape);

      return ret;
    }

    // 打印
    print(){
      console.log(this);
    }
    
    toString(){
      return this.toArray();
    }
  },

  // 返回列表所有元素相乘的结果
  // 如果不是数组则返回arr表示的数字
  "mulAll":function (arr){
    if(arr instanceof Array){
      var m = 1;
      for(let i = 0, il = arr.length; i < il; i++){
        m *= arr[i];
      }
      return m;
    }
    return parseFloat(arr);
  },
  // 全零数组
  "zeros":function(shape){
    let tarr = new TArray.NumArray([], shape);
    tarr.fill(0);
    return tarr;
  },
  // 全一数组
  "ones":function(shape){
    let tarr = new TArray.NumArray([], shape);
    tarr.fill(1);
    return tarr;
  },
  // 数据平铺
  // 参数:
  // A    : 平铺数据源
  // reps : 平铺参数
  // 返回: 
  // 平铺后数组
  "tile":function(A, reps){
    if(!(A instanceof Array)){
      A = [A];
    }

    const tA = new TArray.NumArray(A);

    tA._tile(reps);
    return tA.toArray();
  }
  
}

export default TArray;